package co.recloud.ariadne.request;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;

import co.recloud.ariadne.model.Host;

public class Connection {
	private Socket clientSocket;
	private ObjectInputStream inputStream;
	private ObjectOutputStream outputStream;
	private Host target;
	private Long id;
	/**
	 * @return the clientSocket
	 */
	public Socket getClientSocket() {
		return clientSocket;
	}
	/**
	 * @param clientSocket the clientSocket to set
	 * @throws IOException 
	 */
	public void setClientSocket(Socket clientSocket) throws IOException {
		this.clientSocket = clientSocket;
		inputStream = null;
		outputStream = null;
	}
	/**
	 * @return the inputStream
	 */
	public ObjectInputStream getInputStream() {
		return inputStream;
	}
	/**
	 * @return the outputStream
	 */
	public ObjectOutputStream getOutputStream() {
		return outputStream;
	}
	/**
	 * @return the target
	 */
	public Host getTarget() {
		return target;
	}
	/**
	 * @param target the target to set
	 */
	public void setTarget(Host target) {
		this.target = target;
	}
	/**
	 * @param id the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}
	/**
	 * @return the id
	 */
	public Long getId() {
		return id;
	}

    private void writeToStream(Request request, ObjectOutputStream os) throws SocketException{
        int bufferType = request.getBufferType();
        try {
            os.write(bufferType);
            if(bufferType == Request.SERIAL) {
                os.writeObject(request);
            } else { //PROTOCOL_BUFFER
                byte[] bufferData = request.getMessage().toByteArray();
                os.write(bufferData.length);
                os.write(bufferData);
            }
            os.flush();
            os.reset();
        } catch (SocketException e1) {
            throw e1;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

	public void write(Request request) {
        testSocket();
		if(outputStream == null) {
			try {
				outputStream = new ObjectOutputStream(clientSocket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if(outputStream != null) {
			try {
                writeToStream(request, outputStream);
            } catch (SocketException e) {
                resetConnection();
                try {
                    outputStream = new ObjectOutputStream(clientSocket.getOutputStream());
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                try {
                    writeToStream(request, outputStream);
                } catch (SocketException e2) {
                    e2.printStackTrace();
                }
            } catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

    private void resetConnection() {
        try {
            clientSocket.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        try {
            clientSocket = new Socket(target.getHostName(), target.getPort());
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    private void testSocket() {
        if(!clientSocket.isConnected() || clientSocket.isClosed()) {
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {

            }
            try {
                clientSocket = new Socket(target.getHostName(), target.getPort());
                outputStream = null;
                inputStream = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public Object read() {
        testSocket();
		Object result = null;
		if(inputStream == null) {
			try {
				inputStream = new ObjectInputStream(clientSocket.getInputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if(inputStream != null) {
			try {
				result = inputStream.readObject();
            } catch (SocketException e) {
                resetConnection();
                try {
                    inputStream = new ObjectInputStream(clientSocket.getInputStream());
                } catch (IOException ioe) {
                    e.printStackTrace();
                }
                try {
                    result = inputStream.readObject();
                } catch (IOException e1) {
                    e1.printStackTrace();
                } catch (ClassNotFoundException e1) {
                }
            } catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return result;
	}
}
